<?php

namespace common\service;

use common\exception\LogicException;
use common\model\DemandPoolReportModel;
use common\model\EntityModel;
use common\model\PlanModel;
use common\model\PlanTaskRelationModel;
use common\model\ProjectModel;
use common\model\StepCategoryModel;
use common\model\TaskModel;
use common\model\TaskRelationTagModel;
use support\ErrorCode;
use support\SingletonTrait;

class DemandPoolReportService
{

    use SingletonTrait;

    /**
     * 获取需求池统计列表
     * @param int    $projectId       项目ID
     * @param string $stepCategoryIds 环节ID
     * @param array  $time            时间范围 [开始时间, 结束时间]
     * @param string $attribute       属性
     * @return array
     */
    public function getDemandPoolList(int $projectId, string $stepCategoryIds, array $time, string $attribute = '')
    {
        $where = [];
        //查询条件
        $where['project_id'] = $projectId;
        $where['step_category_id'] = ['in', explode(',', $stepCategoryIds)];
        $where['report_date'] = ['BETWEEN', [$time[0], $time[1]]];
        $where['type'] = ['in', [DemandPoolReportModel::TYPE_STATIC_NUMBER, DemandPoolReportModel::TYPE_STATIC_MINUTE, DemandPoolReportModel::TYPE_STATIC_EPISODE]];
        if ($attribute) {
            $where['attribute'] = $attribute;
        }

        //查询节点列表
        $list = model(DemandPoolReportModel::class)
            ->field('id,report_date,project_id,type,step_category_id,attribute,stage,entity_ids,link_id,episode_code')
            ->where($where)
            ->select();

        //获取团队环节--对映--环节ID
        $teamStepCategoryMap = ProjectTeamService::getInstance()->getTeamStepCategoryMap();
        //环节列表
        $stepCategoryShowList = TemplateService::getInstance()->findStepCategoryShowList();
        $stepCategoryShowList = array_column($stepCategoryShowList, null, 'id');

        //查询比例配制
        $linkIds = array_unique(array_column($list, 'link_id'));
        if (empty($linkIds)) {
            return [];
        }
        $poolRatioConfig = $this->getPoolRatioConfig($linkIds);
        $projectInfo = model(ProjectModel::class)->where(['id' => $projectId])->field('duration,json')->find();

        //整理数组
        foreach ($list as &$val) {
            $val['module_code'] = $stepCategoryShowList[$val['step_category_id']]['module_code'] ?? '';
            $val['entity_ids'] = $this->countEpisodeTime($val, $poolRatioConfig, $projectInfo);
            $val['team_category_id'] = $teamStepCategoryMap[$val['step_category_id']] ?? 0;

            //去除---多余字段
            unset($val['episode_code']);
            unset($val['link_id']);
            unset($val['type']);
            unset($val['module_code']);
        }
        return $list;
    }

    /**
     * 查询比例配制
     * @param array $linkIds 排期IDs
     * @return array
     */
    private function getPoolRatioConfig(array $linkIds)
    {
        $poolRatioList = model(DemandPoolReportModel::class)
            ->field('entity_ids, link_id, attribute,episode_code,report_date')
            ->where(['link_id' => ['in', $linkIds], 'type' => DemandPoolReportModel::TYPE_STATIC_RATIO])
            ->select();

        $ratioMap = [];
        foreach ($poolRatioList as $val) {
            $ratioMap[$val['link_id'] . '#' . $val['episode_code'] . '#' . $val['report_date']] = $val;
        }
        return $ratioMap;
    }

    /**
     * 计算剧集的时长--分钟
     * @param array $poolInfo
     * @return float|int
     */
    private function countEpisodeTime(array $poolInfo, array $poolRatioConfig, array $projectInfo)
    {
        //查询比例配制
        $poolRatioInfo = $poolRatioConfig[$poolInfo['link_id'] . '#' . $poolInfo['episode_code'] . '#' . $poolInfo['report_date']] ?? [];
        if (empty($poolRatioInfo)) {
            return 0;
        }

        //显示分钟 -- 剧集
        switch ($poolInfo['type']) {
            //集
            case DemandPoolReportModel::TYPE_STATIC_EPISODE:
                $num = count(explode(',', $poolRatioInfo['entity_ids'])) * $poolRatioInfo['attribute'];
                $num = sprintf("%.2f", $num);
                break;
            //分钟
            case DemandPoolReportModel::TYPE_STATIC_MINUTE:
                //获取制作周期 --  一集时长
                $duration = $projectInfo['duration'];
                if (empty($duration)) {
                    return 0;
                }
                //计算一集时长
                $list = explode(':', $duration);
                $episodeTime = intval($list[0]) * 60 + intval($list[1]);
                //多少分钟
                $episodeNum = count(explode(',', $poolRatioInfo['entity_ids']));
                $num = round($episodeNum * $episodeTime * $poolRatioInfo['attribute']);
                break;
            default:
                //镜头
                if ($poolInfo['module_code'] == 'shot') {
                    //多少集
                    $num = count(explode(',', $poolRatioInfo['entity_ids'])) * $poolRatioInfo['attribute'];
                    //获取项目--  一集镜头量
                    $json = $projectInfo['json'];
                    if (empty($json)) {
                        return 0;
                    }
                    $shotCountList = json_decode($json, true);
                    $shotCount = $shotCountList['single_episode_shot_count'] ?? 0;
                    return round($shotCount * $num);
                }
                //计算实体数量
                $num = 0;
                if ($poolInfo['entity_ids']) {
                    $num = count(explode(',', $poolInfo['entity_ids']));
                }
                break;
        }
        return $num;
    }

    /**
     * 根据实体IDs -- 同步需求池统计
     * @param array $entityIds 实体IDs
     * @return int
     */
    public function syncDemandPoolByEntityIds(array $entityIds)
    {
        //查询实体的--剧集
        $episodeList = model(EntityModel::class)
            ->field("distinct initial_episode_code, project_id")
            ->where(['id' => ['in', $entityIds]])
            ->select();

        $planIds = [];
        $demandPoolReportModel = model(DemandPoolReportModel::class);
        foreach ($episodeList as $val) {
            //组装数组
            $condition = [];
            $condition['type'] = DemandPoolReportModel::TYPE_STATIC_RATIO;
            $condition['project_id'] = $val['project_id'];
            $condition['episode_code'] = $val['initial_episode_code'];

            //查询所属--排期
            $linkIds = $demandPoolReportModel->field("distinct link_id")->where($condition)->column('link_id');
            if ($linkIds) {
                $planIds = array_merge($planIds, $linkIds);
            }
        }
        $planIds = array_unique($planIds);
        //同步排期---需求池统计
        foreach ($planIds as $val) {
            $this->syncDemandPoolByPlanId($val);
        }
        return 1;
    }

    /**
     * 根据剧集 -- 同步需求池统计
     * @param int    $projectId 项目ID
     * @param string $episodeCode 剧集
     * @return int
     */
    public function syncDemandPoolByEpisode(int $projectId, string $episodeCode)
    {
        //组装数组
        $condition = [];
        $condition['type'] = DemandPoolReportModel::TYPE_STATIC_RATIO;
        $condition['project_id'] = $projectId;
        $condition['episode_code'] = $episodeCode;

        //查询所属--排期
        $linkIds = model(DemandPoolReportModel::class)->field("distinct link_id")->where($condition)->column('link_id');
        //同步排期---需求池统计
        foreach ($linkIds as $val) {
            $this->syncDemandPoolByPlanId($val);
        }
        return 1;
    }

    /**
     * 根据排期ID -- 同步需求池统计
     * @param int $planId 排期ID
     * @return int
     */
    public function syncDemandPoolByPlanId(int $planId)
    {
        //环节全局排期
        $planInfo = model(PlanModel::class)
            ->where(['id' => $planId, 'type' => PlanModel::TYPE_STEP_CATEGORY, 'scope' => PlanModel::SCOPE_GLOBAL])
            ->find();
        if (empty($planInfo)) {
            return 0;
        }

        //获取排期的---剧集、环节、月份比例
        list($episodeList, $stepCategoryIdList, $ratio) = $this->clearPlanInfo($planInfo);
        if (empty($episodeList)) {
            return 0;
        }
        //处理已有排期--属性排期优先
        $this->handleAttributePlanExist($planInfo, $episodeList, $stepCategoryIdList[0]);

        //单集--保存需求统计
        foreach ($episodeList as $episodeCode) {
            if ($episodeCode) {
                $this->saveDemandPoolByEpisode($planInfo, $ratio, $episodeCode, $stepCategoryIdList[0]);
            }
        }
    }

    /**
     * 单集--保存需求统计
     * @param array  $planInfo       排期信息
     * @param array  $ratio          比例
     * @param string $episodeCode    剧集
     * @param int    $stepCategoryId 环节ID
     * @return int
     */
    private function saveDemandPoolByEpisode(array $planInfo, array $ratio, string $episodeCode, int $stepCategoryId)
    {
        //获取环节单位--分钟、 剧集
        $teamInfo = ProjectTeamService::getInstance()->getTeamConfig(0, $stepCategoryId);
        //保存排期--月份比例
        $this->savePlanRatioConfig($planInfo, $ratio, $episodeCode);
        //查询--排期下所有的实体id
        $entityIds = $this->getPlanEntityIds($planInfo, $episodeCode, $stepCategoryId);

        //组装数组--按比例拆分--实体数组
        $entityRatioList = $this->getEntityRatioList($entityIds, $ratio);
        //循环数组
        foreach ($entityRatioList as $val) {
            $data = [
                'step_category_id' => $stepCategoryId,
                'report_date' => $val['month'],
                'episode_code' => $episodeCode,
                'type' => $teamInfo['list'][0]['unit'] ?? DemandPoolReportModel::TYPE_STATIC_NUMBER,
                'entity_ids' => '',
                'attribute' => '',
            ];
            //获取实体列表--属性
            $entityList = $this->getEntityAttrList($val['entity_ids']);
            //镜头环节--剧本环节--插入一条默认数据
            if (empty($entityList)) {
                $this->saveDemandPoolData($planInfo, $data);
            }

            //按属性分组
            $list = arrayGroup($entityList, 'attribute', 'id');
            $attributeList = []; // 属性-列表
            foreach ($list as $attr) {
                //如果排期--有属性--只保存相应属性的实体
                if ($planInfo['attribute'] && $planInfo['attribute'] != $attr['attribute']) {
                    continue;
                }
                $attributeList[] = $attr['attribute'];
                $data['entity_ids'] = $attr['id'];
                $data['attribute'] = $attr['attribute'];
                $this->saveDemandPoolData($planInfo, $data);
            }
            //删除垃圾数据
            if ($attributeList) {
                model(DemandPoolReportModel::class)
                    ->where(['link_id' => $planInfo['id'], 'type' => DemandPoolReportModel::TYPE_STATIC_NUMBER, 'attribute' => ['not in', $attributeList]])
                    ->delete();
            }
        }
        return 1;
    }

    /**
     * 处理已有排期--属性排期优先
     * @param array $planInfo       排期信息
     * @param array $episodeList    剧集列表
     * @param int   $stepCategoryId 环节ID
     * @return void
     */
    private function handleAttributePlanExist(array $planInfo, array $episodeList, int $stepCategoryId)
    {
        //判断排期环节--是否有属性
        if (empty($planInfo['attribute'])) {
            return;
        }

        //条件
        $condition = [
            'project_id' => $planInfo['project_id'],
            'step_category_id' => $stepCategoryId,
            'attribute' => $planInfo['attribute'],
            'episode_code' => ['in', $episodeList],
        ];
        //有判断是否--删除已有统计
        model(DemandPoolReportModel::class)->where($condition)->delete();
    }

    /**
     * 保存需求池数据
     * @param array $planInfo 排期信息
     * @param array $data     更新数据
     * @return int
     */
    private function saveDemandPoolData(array $planInfo, array $data)
    {
        $demandPoolReportModel = model(DemandPoolReportModel::class);
        //判断是否已相同统计
        $hasInfo = $demandPoolReportModel->field('id,link_id')
            ->where([
                'project_id' => $planInfo['project_id'],
                'attribute' => $data['attribute'],
                'episode_code' => $data['episode_code'],
                'step_category_id' => $data['step_category_id']
            ])
            ->find();
        if (!empty($hasInfo) && $hasInfo['link_id'] != $planInfo['id']) {
            return 1;
        }

        //组装数组---保存
        $addData = [
            'project_id' => $planInfo['project_id'],
            'step_category_id' => $data['step_category_id'],
            'attribute' => $data['attribute'],
            'episode_code' => $data['episode_code'],
            'report_date' => $data['report_date'],
            'type' => $data['type'],
        ];
        //查询是否已有记录
        $hasInfo = $demandPoolReportModel->field('id')->where($addData)->find();
        //修改数据
        $addData['link_id'] = $planInfo['id'];
        $addData['entity_ids'] = $data['entity_ids'];
        $addData['created'] = time();
        if (empty($hasInfo)) {
            $demandPoolReportModel->add($addData);
        } else {
            //更新排期
            $demandPoolReportModel->where(['id' => $hasInfo['id']])->save($addData);
        }
        return 1;
    }

    /**
     * 整理--排期信息
     * @param array $info 排期信息
     * @return array|int
     */
    private function clearPlanInfo(array $info)
    {
        //查询排期--剧集名称code
        $episodeList = model(PlanTaskRelationModel::class)->alias('ptr')
            ->join("task_relation_tag trt on trt.task_id = ptr.task_id")
            ->join("entity on trt.link_id = entity.id", 'left')
            ->field('entity.name')
            ->where(['ptr.plan_id' => $info['id'], 'trt.link_module_code' => 'episode'])
            ->column('name');
        if (empty($episodeList)) {
            return 0;
        }
        //查询排期下的--环节ID--- 单一
        $stepCategoryIdList = model(TaskRelationTagModel::class)
            ->where(['task_id' => $info['task_id'], 'link_module_code' => ['in', ['show_step_category', 'step_category']]])
            ->field('link_id')
            ->column('link_id');

        //获取环节单位--分钟、 剧集
        $teamInfo = ProjectTeamService::getInstance()->getTeamConfig(0, $stepCategoryIdList[0]);

        //获得--排期月份比例
        $ratio = $this->getPlanRatioInfo($info, $teamInfo['list'][0]['unit'] ?? DemandPoolReportModel::TYPE_STATIC_NUMBER);

        return [$episodeList, $stepCategoryIdList, $ratio];
    }

    /**
     * 获取--排期下所有的实体id
     * @param array  $info        详情
     * @param string $episodeCode 剧集名
     * @return array
     */
    private function getPlanEntityIds(array $info, string $episodeCode, int $stepCategoryId)
    {
        //环节列表
        $stepCategoryShowList = TemplateService::getInstance()->findStepCategoryShowList();

        $stepCategoryShowList = array_column($stepCategoryShowList, null, 'id');
        $moduleCode = $stepCategoryShowList[$stepCategoryId]['module_code'] ?? '';

        //属于资产的---才统计实体数
        if (!in_array($moduleCode, ['design', 'asset', 'level'])) {
            return [];
        }

        //查询--排期下所有的--资产实体id
        $condition = [];
        $condition['project_id'] = $info['project_id'];
        $condition['initial_episode_code'] = $episodeCode;
        $condition['module_id'] = module_by_code('design')["id"];
        //发布的实体
        $condition['is_generate'] = 'yes';
        //关卡环节只统计---关卡实体
        if ($moduleCode == 'level') {
            $condition['_string'] = "JSON_UNQUOTE(JSON_EXTRACT(json,'$.design_category')) = '关卡'";
        }

        return model(EntityModel::class)->field("id")->where($condition)->column('id');
    }

    /**
     * 保存排期--时间比例
     * @param array  $info         排期信息
     * @param array  $ratioList    比例列表
     * @param string $episodeCodes 剧集
     * @return int
     */
    private function savePlanRatioConfig(array $info, array $ratioList, string $episodeCodes)
    {
        $demandPoolReportModel = model(DemandPoolReportModel::class);
        $monthList = [];
        foreach ($ratioList as $key => $val) {
            $monthList[] = $key;
            //组装数组
            $addData = [
                'report_date' => $key,
                'project_id' => $info['project_id'],
                'link_id' => $info['id'],
                'episode_code' => $episodeCodes,
                'type' => DemandPoolReportModel::TYPE_STATIC_RATIO,
            ];
            //查询是否已有记录
            $hasInfo = $demandPoolReportModel->field('id,attribute')
                ->where($addData)
                ->find();
            //修改数据
            $addData['attribute'] = $val;
            if (empty($hasInfo)) {
                $addData['created'] = time();
                $demandPoolReportModel->add($addData);
            } else {
                if ($hasInfo['attribute'] != $val) {
                    $demandPoolReportModel->where(['id' => $hasInfo['id']])->save($addData);
                }
            }
        }
        //删除--多余的月份
        $reportDate = $demandPoolReportModel
            ->field('report_date')
            ->where(['link_id' => $info['id'], 'type' => DemandPoolReportModel::TYPE_STATIC_RATIO])
            ->column('report_date');

        $delMonths = array_diff($reportDate, $monthList);
        if ($delMonths) {
            $demandPoolReportModel->where(['link_id' => $info['id'], 'report_date' => ['in', $delMonths]])->delete();
        }

        return 1;
    }

    /**
     * 获取实体列表--属性
     * @param array $entityIds 实体IDs
     * @return array
     */
    private function getEntityAttrList(array $entityIds)
    {
        if (empty($entityIds)) {
            return [];
        }
        //按属性分组
        $entityList = model(EntityModel::class)
            ->field("id,module_id,json")
            ->where(['id' => ['in', $entityIds]])
            ->select();
        //属性抽出来
        foreach ($entityList as &$entity) {
            $code = module_by_id($entity['module_id'])['code'];

            $json = json_decode($entity['json'], true);
            $entity['attribute'] = $json[$code . '_category'] ?? '';
        }
        return $entityList;
    }

    /**
     * 组装数组--按比例拆分数组
     * @param array $entityIds 实体ID列表
     * @param array $ratioList 时间比例数组
     * @return array
     */
    private function getEntityRatioList(array $entityIds, array $ratioList)
    {
        $rt = [];

        $i = 0;
        //多少个实体
        $num = count($entityIds);
        foreach ($ratioList as $key => $val) {
            $rt[$i]['ratio'] = $val;
            $rt[$i]['month'] = $key;
            $rt[$i]['entity_ids'] = [];
            $entityNum = round($val * $num);
            if ($entityNum) {
                $sliceEntityIds = array_slice($entityIds, 0, $entityNum);
                $rt[$i]['entity_ids'] = $sliceEntityIds;
                //剩余数组
                $entityIds = array_diff($entityIds, $sliceEntityIds);
            }
            $i++;
        }
        //有剩余赋给--第一个月
        if ($entityIds) {
            $rt[0]['entity_ids'] = array_merge($rt[0]['entity_ids'], $entityIds);
        }
        return $rt;
    }


    /**
     * 获得--排期月份比例
     * @param array $planInfo 排期信息
     * @param int $unit 单位
     * @return array
     */
    private function getPlanRatioInfo(array $planInfo, int $unit)
    {
        //查询排期详情
        $startMonth = strtotime(date('Y-m-01', $planInfo['start_time']));
        $endMonth = strtotime(date('Y-m-01', $planInfo['end_time']));
        if ($startMonth == $endMonth) {
            return [
                $endMonth => 1,
            ];
        }
        //集--按开始时间
        if ($unit == DemandPoolReportModel::TYPE_STATIC_EPISODE) {
            return [
                $startMonth => 1,
            ];
        }

        //计算开始时间到月末的天数
        $startDayNum = intval(strtotime(date('Y-m-t', $planInfo['start_time'])) - $planInfo['start_time']) / 86400;
        //计算结束时间到月末的天数
        $endDayNum = intval($planInfo['end_time'] - strtotime(date('Y-m-01', $planInfo['end_time']))) / 86400;
        //计算总天数
        $allDayNum = intval(($planInfo['end_time'] - $planInfo['start_time']) / 86400);
        //计算比例
        $startRatio = round($startDayNum / $allDayNum, 2);
        $endRatio = round($endDayNum / $allDayNum, 2);
        //还剩多少个天
        $leftDayNum = $allDayNum - $endDayNum - $startDayNum;
        //还剩多少个月
        $leftMontNum = round($leftDayNum / 30);
        if (empty($leftMontNum)) {
            return [
                $startMonth => $startRatio,
                $endMonth => $endRatio
            ];
        }

        //循环剩余月
        $rt = [];
        $leftRatio = round((1 - $startRatio - $endRatio) / $leftMontNum, 2);
        $rt[$startMonth] = $startRatio;
        for ($i = 1; $i <= $leftMontNum; $i++) {
            $forMonth = strtotime("+{$i} months", $startMonth);
            $rt[$forMonth] = $leftRatio;
        }
        $rt[$endMonth] = $endRatio;
        return $rt;
    }

}
